#ifndef _LOG_HPP_
#define _LOG_HPP_

#include <iostream>
#include <cstdio>
#include <string>
#include <filesystem> //C++17
#include <sstream>
#include <fstream>
#include <memory>
#include <ctime>
#include <unistd.h>
#include "Mutex.hpp"

using namespace std;
using namespace MutexModule;

const string grep = "\r\n";
const string defaultpath = "./log";  // 路径
const string defaultfile = "my.log"; // 文件

namespace LogModule
{
    class Logstrategy // 基类
    {
    public:
        ~Logstrategy() = default;                        // 编译器自动生成该析构函数
        virtual void syncloy(const string &message) = 0; // 纯虚继承
    };

    class ConsoleLogstrateg : public Logstrategy // 屏幕打印派生类
    {
    public:
        ConsoleLogstrateg()
        {
        }

        void syncloy(const string &message) override // 检测继承是否是继承的基类
        {
            LockGuard lockguard(_mutex);
            cout << message << grep;
        }

        ~ConsoleLogstrateg()
        {
        }

    private:
        Mutex _mutex;
    };

    class FileLogstrateg : public Logstrategy // 指定文件打印的派生类
    {
    public:
        FileLogstrateg(string defaultpath, string defaultfile)
            : _path(defaultpath), _file(defaultfile)
        {
            LockGuard lockguard(_mutex);
            if (filesystem ::exists(_path)) // 判断路径存不存在 ，为真不存在retuen，创建新路径
            {
                return;
            }
            try
            {
                filesystem ::create_directories(_path); // 创建新路径
            }
            catch (const filesystem ::filesystem_error &e) // 没创建成功捕捉异常
            {
                cerr << e.what() << "\n";
            }
        }

        void syncloy(const string &message) override
        {
            LockGuard lockguard(_mutex);
            string filename = _path + _file;  // 文件名
            ofstream out(filename, ios::app); // 追加写入的方式打开文件
            if (!out.is_open())
            {
                return;
            }

            out << message << grep;
            out.close();
        }

        ~FileLogstrateg()
        {
        }

    private:
        string _path;
        string _file;
        Mutex _mutex;
    };

    enum class LogLevel // 枚举错误类型
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    string Leve12str(const LogLevel &level) // 为避免枚举是整数
    {
        switch (level)
        {
        case LogLevel ::DEBUG:
            return "DEBUG";
        case LogLevel ::INFO:
            return "INFO";
        case LogLevel ::FATAL:
            return "FATAL";
        case LogLevel ::WARNING:
            return "WARNING";
        case LogLevel ::ERROR:
            return "ERROR";
        default:
            return "UNKNOWN";
        }
    }

    string GetTimestamp() // 获取时间
    {
        time_t curr = time(nullptr);
        struct tm curr_tm;
        localtime_r(&curr, &curr_tm);
        char timebuffer[128];
        snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d_%02d:%02d:%02d",
                 curr_tm.tm_year+1900,
                 curr_tm.tm_mon + 1,
                 curr_tm.tm_mday,
                 curr_tm.tm_hour,
                 curr_tm.tm_min,
                 curr_tm.tm_sec);

        return string(timebuffer);
    }

    class Logger
    {
    public:
        Logger()
        {
            EnableFileLogstrateg();
        }
        void EnableConsoleLogstrateg()
        {
            _fflush_strategy = make_unique<ConsoleLogstrateg>(); // c++14语法通过指针创建并初始化指针对象
        }

        void EnableFileLogstrateg()
        {
            _fflush_strategy = make_unique<FileLogstrateg>(defaultpath + "/", defaultfile);
        }

        class LogMessage
        {
        public:
            LogMessage(LogLevel level, string &src_name, int line_number, Logger &logger)
                : _curr_time(GetTimestamp()), _level(level), _pid(getpid()), _src_name(src_name), _line_number(line_number), _logger(logger)
            {
                stringstream ss;
                ss << "[" << _curr_time << "]"
                   << "[" << Leve12str(_level) << "]"
                   << "[" << _pid << "]"
                   << "[" << _src_name << "]"
                   << "[" << _line_number << "]";

                _loginfo = ss.str();
            }

            template <class T>
            LogMessage &operator<<(const T &info) // 重载
            {
                stringstream ss;
                ss << info;
                _loginfo += ss.str();
                return *this;
            }

            ~LogMessage()
            {
                if (_logger._fflush_strategy)
                {
                    _logger._fflush_strategy->syncloy(_loginfo);
                }
            }

        private:
            string _curr_time;
            LogLevel _level;
            pid_t _pid;
            string _src_name;
            int _line_number;
            string _loginfo; // 合并后的完整信息
            Logger &_logger;
        };

        // 不写&,故意创建临时对象
        LogMessage operator()(LogLevel level, std::string name, int line) // 仿函数调用的时候创建临时对象，对单个日志进行构造
        {
            return LogMessage(level, name, line, *this);
        }

        ~Logger()
        {
        }

    private:
        unique_ptr<Logstrategy> _fflush_strategy; // 智能指针创建指针
    };

    // 全局日志对象
    Logger logger;

// 使用宏，简化用户操作，获取文件名和行号
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Console_Log_Strategy() logger.EnableConsoleLogstrateg()
#define Enable_File_Log_Strategy() logger.EnableFileLogstrateg()

}

#endif
